package de.hs_mannheim.visualscheduler.scheduling;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

/**
 * The Class SchedulingResult contains statistics and data for a scheduling process.
 */
public class SchedulingResult extends ArrayList<Timeblock> {
	private static final long				serialVersionUID	= -1132656016774615900L;
	private final ArrayList<ProcessResult>	processes			= new ArrayList<ProcessResult>();
	private int								endTime				= 0;
	private double							averageLatency		= 0.0;
	private double							averageSleep		= 0.0;
	private double							averageActive		= 0.0;

	/**
	 * Gets the end time of all processes.
	 * 
	 * @return the end time
	 */
	public int getEndTime() {
		return endTime;
	}

	/**
	 * Gets the average latency.
	 * 
	 * @return the average latency
	 */
	public double getAverageLatency() {
		return averageLatency;
	}

	/**
	 * Gets the average runtime.
	 * 
	 * @return the average runtime
	 */
	public double getAverageRuntime() {
		return averageLatency + averageSleep + averageActive;
	}

	/**
	 * Gets the average wait time.
	 * 
	 * @return the average wait time
	 */
	public double getAverageWait() {
		return averageLatency + averageSleep;
	}

	/**
	 * Gets the average sleep time.
	 * 
	 * @return the average sleep time
	 */
	public double getAverageSleep() {
		return averageSleep;
	}

	/**
	 * Gets the average active time.
	 * 
	 * @return the average active time
	 */
	public double getAverageActive() {
		return averageActive;
	}

	private String getPercentString(double val){
		String percStr = "" + (int) (val * 10000.0 / getAverageRuntime()) / 100.0;
		String numStr = "" + val;
		return numStr.substring(0, Math.min(numStr.length(), 9)) + " (" + percStr.substring(0, Math.min(percStr.length(), 5)) + "%)";
	}
	
	/**
	 * Gets the average latency percent string.
	 * 
	 * @return the average latency percent string
	 */
	public String getAverageLatencyPercentString() {
		return getPercentString(averageLatency);
	}

	/**
	 * Gets the average wait percent string.
	 * 
	 * @return the average wait percent string
	 */
	public String getAverageWaitPercentString() {
		return getPercentString(getAverageWait());
	}

	/**
	 * Gets the average sleep percent string.
	 * 
	 * @return the average sleep percent string
	 */
	public String getAverageSleepPercentString() {
		return getPercentString(averageSleep);
	}

	/**
	 * Gets the average active percent string.
	 * 
	 * @return the average active percent string
	 */
	public String getAverageActivePercentString() {
		return getPercentString(averageActive);
	}

	/**
	 * Gets the processes.
	 * 
	 * @return the processes
	 */
	public ArrayList<ProcessResult> getProcesses() {
		return processes;
	}

	/**
	 * Update statistics. <b>This has to be called after setting the data</b>
	 */
	public void update() {
		// Initialize all Values to their defaults
		endTime = 0;
		processes.clear();
		averageActive = 0.0;
		averageSleep = 0.0;
		averageLatency = 0.0;

		if (size() == 0)
			return;

		// Sort processes by time of arrival
		final ProcessDefinition[] results = get(0).keySet().toArray(new ProcessDefinition[get(0).size()]);
		Arrays.sort(results, new Comparator<ProcessDefinition>() {
			@Override
			public int compare(final ProcessDefinition o1, final ProcessDefinition o2) {
				return o1.getArrival() - o2.getArrival();
			}
		});
		for (final ProcessDefinition pd : results) {
			processes.add(new ProcessResult(pd));
		}

		// Calculate global and process-specific statistics
		for (final Timeblock tb : this) {
			endTime = Math.max(tb.getStartTime() + tb.getDuration(), endTime);
			for (final ProcessResult pr : processes) {
				final ProcessState state = tb.get(pr);
				if (state == ProcessState.WAITING_FOR_START) {
					pr.latency += tb.getDuration();
					averageLatency += tb.getDuration();
				} else if (state == ProcessState.STARTED_AND_WAITING) {
					pr.sleep += tb.getDuration();
					averageSleep += tb.getDuration();
				} else if (state == ProcessState.STARTED_AND_RUNNING) {
					pr.active += tb.getDuration();
					averageActive += tb.getDuration();
				}
			}
		}
		averageActive /= processes.size();
		averageSleep /= processes.size();
		averageLatency /= processes.size();
	}
}
